#include "AReVi/arObject.h"
#include "AReVi/Utils/abstractStream.h"
using namespace AReVi;

#include "environment.h"
#include "event.h"
#include "element.h"
#include "manager.h"
#include "connector.h"

AR_CLASS_DEF(Environment, ArObject)

Environment::Environment(ArCW & arCW) : ArObject(arCW) {
    // On s'enregistre pour recevoir les évènements qui nous intéressent
    // - Répondre à Environment.GET_LIST
    Manager::getInstance()->getConnector()->addCallback("Environment", StlString("GET_LIST"), thisPtr(), &Environment::_onListRequest);
    // - Traiter la liste Environment.LIST des éléments
    Manager::getInstance()->getConnector()->addCallback("Environment", StlString("LIST"), thisPtr(), &Environment::_onList);
    // - Traiter la création d'un élément
    Manager::getInstance()->getConnector()->addCallback("CREATE", false, thisPtr(), &Environment::_onElementCreate);
    // - Traiter la réception d'informations sur un élément
    Manager::getInstance()->getConnector()->addCallback("INFO", false, thisPtr(), &Environment::_onElementInfo);
    // - Traiter la suppression d'un élément
    Manager::getInstance()->getConnector()->addCallback("DELETE", false, thisPtr(), &Environment::_onElementDelete);
}

Environment::~Environment() {
}


bool Environment::hasElement(StlString id)
{
  for(uint i=0; i<_elements.size(); i++)
    if(_elements[i]->getID() == id)
      return true;
  return false;
}

ArRef<Element> Environment::getElementByID(StlString id)
{
  for(uint i=0; i<_elements.size(); i++)
    if(_elements[i]->getID() == id)
      return _elements[i];
  return Element::nullRef();
}

void Environment::addElement(ArRef<Element> element) {
    if (!element) return;
cerr << "DEBUG: Adding element " << element->getID() << endl;
    if (hasElement(element->getID())) return;
    if (! element->isLocal()) {
        cerr << "internal: Trying to add distant element !" << endl;
        return;
    }
    _elements.push_back(element);
    
    // Je m'enregistre pour répondre aux infos de cet objet
    Manager::getInstance()->getConnector()->addCallback(element->getID(), "GET_INFO", thisPtr(), &Environment::_onElementInfoRequest);
    
    // Je demande à l'objet de se déclarer créé
    element->fireCreate();

}

void Environment::removeElement(ArRef<Element> element) {
    StlVector<ArRef<Element> >::iterator it = _elements.begin();
    while (it != _elements.end()) {
        if (*it == element) {
            _elements.erase(it);
            element->fireDelete();
            return;
        }
        it++;
    }
}


void Environment::clearLocal() {
    StlVector<ArRef<Element> >::iterator it = _elements.begin();
    while (it != _elements.end()) {
        if ((*it)->isLocal()) {
            (*it)->fireDelete();
            _elements.erase(it);
            return;
        }
        it++;
    }
}

void Environment::_onListRequest(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getEmitterID() != "Environment" || evt->getType() != "GET_LIST") {
        cerr << "internal: Bad event for list request" << endl;
        return;
    }
#endif
    ArRef<Event> response = Event::NEW();
    response->setEmitterID("Environment");
    response->setType("LIST");
    ArRef<AbstractOStream> stream = response->getOStream();

    StlString list;
    int nb = 0;
    StlVector<ArRef<Element> >::iterator it = _elements.begin();
    while (it != _elements.end()) {
        if ((*it)->isLocal()) {
            list += (*it)->getID() + "\n";
            nb++;
        }
        it++;
    }
    stream->writeInt(nb);
    stream->writeTxtString(list);
    
    Manager::getInstance()->broadcast(response);
}

void Environment::_onList(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getEmitterID() != "Environment" || evt->getType() != "LIST") {
        cerr << "internal: Bad event for list : " << evt->getEmitterID() << "->" << evt->getType() << endl;
        return;
    }
#endif
    ArRef<AbstractIStream> stream = evt->getIStream();
    int nb = 0;
    if (! stream->readInt(nb)) return;
    
    while (nb > 0) {
        StlString line;
        if (stream->readTxtLine(line) && !hasElement(line)) {
            ArRef<Event> response = Event::NEW();
            response->setEmitterID(line);
            response->setType("GET_INFO");
            Manager::getInstance()->broadcast(response);
        }
        nb--;
    }
}

void Environment::_onElementInfoRequest(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getType() != "GET_INFO") {
        cerr << "internal: Bad event for info request" << endl;
        return;
    }
#endif
    ArRef<Element> element = getElementByID(evt->getEmitterID());
    if (! element || ! element->isLocal()) return; // NOT AN ERROR
    element->fireInfo();
}

void Environment::_onElementCreate(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getType() != "CREATE") {
        cerr << "internal: Bad event for info request" << endl;
        return;
    }
#endif
    if (hasElement(evt->getEmitterID())) {
        cerr << "internal: Element allready created" << endl;
        return;
    }
    
    ArRef<Element> element = Element::createFromEvent(evt);
    if (! element) {
        cerr << "internal: Unable to create element" << endl;
        return;
    }
    _elements.push_back(element);
}

void Environment::_onElementInfo(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getType() != "INFO") {
        cerr << "internal: Bad event for info treatment" << endl;
        return;
    }
#endif
    if (hasElement(evt->getEmitterID())) return; // NOT AN ERROR
    
    ArRef<Element> element = Element::createFromEvent(evt);
    if (! element) {
        cerr << "internal: Unable to create element" << endl;
        return;
    }
    _elements.push_back(element);
}

void Environment::_onElementDelete(ArRef<Event> evt) {
#ifndef NDEBUG
    if (evt->getType() != "DELETE") {
        cerr << "internal: Bad event for deletion request" << endl;
        return;
    }
#endif
    StlVector<ArRef<Element> >::iterator it = _elements.begin();
    while (it != _elements.end()) {
        if ((*it)->getID() == evt->getEmitterID()) {
            if ((*it)->isLocal()) {
                cerr << "internal: Removing local element on DELETE event" << endl;
            }
            if ((*it)->_actUpdateLocation) {
            	(*it)->_actUpdateLocation->abort();
            	(*it)->_actUpdateLocation = Activity::nullRef();
            }
            _elements.erase(it);
        }
        else it++;
    }
}

